﻿///////////////////////////..........
//
//  Author: Mayur Sirwani
//  Date:   23-Oct-2012
//  Desc: This lib is used to hit RESTful services using "multipart/form-data"
//
//////////////////////////..........

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Collections;
using System.Windows.Controls;
using System.IO.IsolatedStorage;

namespace System.Net
{
    public class MultipartHttpClient
    {
        private HttpWebRequest httpWebRequest = null;
        private Dictionary<String, String> postDataDictionary = null;
        private Dictionary<String, String> postFileDictionary = null;
        public String errorMsg { get; set; }
        private Stream stream = null;

        public delegate void MultipartHttpClientHandler(object sender, MultiPartHttpEventArgs e);
        public event MultipartHttpClientHandler ProcessCompleted;

        public MultipartHttpClient(String url, String method)
        {
            this.httpWebRequest = HttpWebRequest.CreateHttp(new Uri(url));
            this.httpWebRequest.Method = method;
        }

        public void AddParameter(String Name, String Value)
        {
            if (this.postDataDictionary == null)
                this.postDataDictionary = new Dictionary<String, String>();
            this.postDataDictionary.Add(Name, Value);
        }

        public void AddFile(String filePath, String MIMEType)
        {
            if (this.postFileDictionary == null)
                this.postFileDictionary = new Dictionary<String, String>();
            this.postFileDictionary.Add(filePath, MIMEType);
        }

        public void MakeRequest()
        {
            if (this.httpWebRequest.Method.ToLower() == "post")
                this.httpWebRequest.BeginGetRequestStream(new AsyncCallback(this.GetRequestStreamCallback), this.httpWebRequest);
            else
                this.httpWebRequest.BeginGetResponse(new AsyncCallback(this.GetResponseCallback), this.httpWebRequest);
        }

        private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
        {
            Int64 boundary = DateTime.Now.Ticks;
            HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
            request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
            Stream postStream = request.EndGetRequestStream(asynchronousResult);

            StringBuilder postData = new StringBuilder();
            int paramCurrCount = 1;
            postData.Append("--" + boundary + "\r\n");

            if (postDataDictionary != null)
            {
                foreach (KeyValuePair<String, String> data in postDataDictionary)
                {
                    postData.Append("Content-Disposition: form-data; name=\"" + data.Key + "\"\r\n\r\n");
                    postData.Append(data.Value);

                    if (paramCurrCount == postDataDictionary.Count && postFileDictionary != null)
                        postData.Append("\r\n--" + boundary + "\r\n");
                    else
                        postData.Append("\r\n--" + boundary + "--\r\n");
                    paramCurrCount++;
                }
            }

            byte[] byteArray = Encoding.UTF8.GetBytes(postData.ToString());
            postStream.Write(byteArray, 0, byteArray.Length);

            byte[] fileBytesArray;
            byte[] boundryEnd = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
            byte[] boundry = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
            int fileCurrentCount = postFileDictionary != null ? 1 : 0;

            if (postFileDictionary != null)
            {
                foreach (KeyValuePair<String, String> file in postFileDictionary)
                {
                    postData = new StringBuilder();
                    postData.Append("Content-Disposition: form-data; name=\"file\"; filename=\"" + GetFileNameFromPath(file.Key) + "\"\r\n");
                    postData.Append("Content-Type: " + file.Value + "\r\n");
                    postData.Append("Content-Transfer-Encoding: utf8\r\n\r\n");

                    byteArray = Encoding.UTF8.GetBytes(postData.ToString());
                    postStream.Write(byteArray, 0, byteArray.Length);

                    fileBytesArray = GetBytesArrayFromFile(file.Key);
                    postStream.Write(fileBytesArray, 0, fileBytesArray.Length);

                    if (fileCurrentCount == postFileDictionary.Count)
                        postStream.Write(boundryEnd, 0, boundryEnd.Length);
                    else
                        postStream.Write(boundry, 0, boundry.Length);
                    fileCurrentCount++;
                }
            }
            if (postFileDictionary == null && postDataDictionary == null)
            {
                postStream.Write(boundryEnd, 0, boundryEnd.Length);
            }

            postStream.Close();

            request.BeginGetResponse(new AsyncCallback(this.GetResponseCallback), request);
        }

        private void GetResponseCallback(IAsyncResult asynchronousResult)
        {
            HttpWebResponse response = null;
            MemoryStream memoryStream = null;

            try
            {
                HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;

                // End the operation
                response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
                stream = response.GetResponseStream();
                memoryStream = new MemoryStream();
                stream.CopyTo(memoryStream);
            }
            catch (Exception ex) { errorMsg = ex.Message; }
            finally
            {
                if (response != null)
                    response.Close();
            }

            MultipartHttpClientHandler handler = ProcessCompleted;
            MultiPartHttpEventArgs myArgs = new MultiPartHttpEventArgs(memoryStream.ToArray());
            memoryStream.Close();
            if (handler != null) handler(this, myArgs);

        }

        private byte[] GetBytesArrayFromFile(String filePath)
        {
            using (var isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (var isolatedFileStream = new IsolatedStorageFileStream(filePath, FileMode.Open, isolatedStorage))
                {
                    var memoryStream = new MemoryStream();
                    isolatedFileStream.CopyTo(memoryStream);
                    return memoryStream.ToArray();
                }
            }
        }

        private String GetFileNameFromPath(String filePath)
        {
            return filePath.Substring(filePath.LastIndexOf("/") + 1, (filePath.Length - filePath.LastIndexOf("/") - 1));
        }
    }

    public class MultiPartHttpEventArgs : EventArgs
    {
        private byte[] _resultBytes;
        private String _error;

        public MultiPartHttpEventArgs(byte[] byteData)
        {
            try
            {
                this._resultBytes = byteData;
            }
            catch (Exception ex) { _error = ex.Message; }
        }

        public byte[] ByteResult { get { return _resultBytes; } }
        public String ExceptionError { get { return _error; } }
    }
}
